/**
 * generator module
 *
 * @author strlst <e11907086@student.tuwien.ac.at>
 * @date 2020-11-10
 * @brief contains methods for setup of shared memory and semaphores, which are utilized to write data to a shared memory circular_buffer
 * @details none
 */

#include "generator.h"

void generate_solution(struct graph* g) {
    /* assign coloring */
    assign_random_coloring(g);

    /* display graph textually */
    print_graph(g);

    size_t invalid_edge_index = 0;
    while ((invalid_edge_index = find_invalid_edge(g)) != INVALID_EDGE_INDEX) {
        printf("edge %li removed\n", invalid_edge_index / 2);
        remove_edge(g, invalid_edge_index);
    }

    /* display graph after the fact */
    print_graph(g);
}

void generate_solutions(struct graph* g, struct circular_buffer *buffer, sem_t *s_access, sem_t *s_used, sem_t *s_free) {
    /* begin generating solutions */
    int i = 0;
    pid_t pid = getpid();
    while (!buffer->quit) {
        printf("solution %i @ PID%i\n", i++, pid);
        /* copy graph as structure is modified on upon access */
        struct graph *g_prime = malloc(sizeof (*g));
        memcpy(g_prime, g, sizeof (*g));

        /* generate solution */
        generate_solution(g_prime);

        /* write solution to circular buffer */
        printf("try produce @ PID%i\n", pid);
        if (sem_wait(s_free) == -1)
            printf("sem_wait(s_free) error\n");
        printf("try write @ PID%i\n", pid);
        if (sem_wait(s_access) == -1)
            printf("sem_wait(s_access) error\n");

        /* write all the edges */
        printf("begin writing @ PID%i, buffer->in: %li\n", pid, buffer->in);
        int e_write = 0;
        for (int e = 0; e < (g_prime->edge_count); e += 2) {
            /* skip non-deleted edges */
            if (g_prime->invalidated[e]     != INVALID_EDGE ||
                g_prime->invalidated[e + 1] != INVALID_EDGE)
                continue;

            /* write edge */
            buffer->edges[buffer->in][e_write]     = g_prime->edges[e];
            buffer->edges[buffer->in][e_write + 1] = g_prime->edges[e + 1];
            e_write += 2;
        }
        /* write delimiting edge */
        buffer->edges[buffer->in][e_write]     = DELIMITER_EDGE;
        buffer->edges[buffer->in][e_write + 1] = DELIMITER_EDGE;
        /* increment solution write index */
        buffer->in = (buffer->in + 1) % MAX_SOLUTIONS;
        printf("stop writing @ PID%i, buffer->in: %li\n", pid, buffer->in);

        /* finalize shared memory access */
        if (sem_post(s_access) == -1)
            printf("sem_post(s_access) error\n");
        printf("write done @ PID%i\n", pid);
        if (sem_post(s_used) == -1)
            printf("sem_post(s_used) error\n");
        printf("produce done @ PID%i\n", pid);

        /* free modified copied graph */
        free(g_prime);
    }

    printf("exit @ PID%i\n", pid);
}

int generate_solutions_setup(struct graph* g) {
    /* create/open shared memory object */
    int shmfd = shm_open(SHM_NAME, O_RDWR, S_IRUSR | S_IWUSR);
    /* die on error */
    if (shmfd == -1)
        return OS_SHM_OPEN_FAILED;

    /* map shared memory object */
    struct circular_buffer *buffer = mmap(
        NULL,
        sizeof(*buffer),
        PROT_READ | PROT_WRITE,
        MAP_SHARED,
        shmfd,
        0
    );

    /* verify that map was successful */
    if (buffer == MAP_FAILED)
        return OS_MAP_FAILED;

    /* open semaphores */
    /* access as mutex, used and free as counters */
    sem_t *s_access = sem_open(
        SEM_ACCESS, O_RDWR
    );
    sem_t *s_used = sem_open(
        SEM_USED, O_RDWR
    );
    sem_t *s_free = sem_open(
        SEM_FREE, O_RDWR
    );
    /* verify semaphores were opened successfully */
    if (s_access   == SEM_FAILED ||
        s_used     == SEM_FAILED ||
        s_free     == SEM_FAILED)
        return OS_SEM_OPEN_FAILED;

    /* start generating solutions */
    generate_solutions(g, buffer, s_access, s_used, s_free);

    /* close semaphores and check for errors */
    if (sem_close(s_access)   == -1 ||
        sem_close(s_used)     == -1 ||
        sem_close(s_free)     == -1)
        return OS_SEM_CLOSE_FAILED;

    /* close shared memory file descriptor */
    if (close(shmfd) == -1)
        return OS_SHM_CLOSE_FAILED;

    /* unmap shared memory object */
    if (munmap(buffer, sizeof(*buffer)) == -1)
        return OS_UNMAP_FAILED;

    return OS_SUCCESS;
}
